﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Collections.ObjectModel;

namespace Djs.Neuronic.Brain
{
    /// <summary>
    /// Generický List, plně kompatibilní se základním Listem.
    /// Nad jeho rámec zajišťuje evidenci smazaných položek a příznaků (Has*).
    /// V případě potřeby nabídne eventy, které hlásí provedení změn.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class DataList<T> : IList<T>, ICollection<T>, IEnumerable<T>, IList, ICollection, IEnumerable
    {
        #region Public metody a property
        /// <summary>
        /// Do Listu vloží dané položky.
        /// Před tím celý seznam vyčistí, a na závěr ho uvede do čistého stavu = AcceptChanges().
        /// To však neznamená, že jednotlivé položky se tímto uloží do databáze; jejich IsDirty ani Id=0 se nezmění.
        /// </summary>
        /// <param name="items"></param>
        public void Store(IEnumerable<T> items)
        {
            this.Clear();
            this.Items.AddRange(items);
        }
        /// <summary>
        /// Akceptuje současný stav = zahodí seznam smazaných záznamů, a shodí všechny změnové příznaky.
        /// To však neznamená, že jednotlivé položky se tímto uloží do databáze; jejich IsDirty ani Id=0 se nezmění.
        /// </summary>
        public void AcceptChanges()
        {
            this.Removed.Clear();
            this._Flags = 0;
        }
        /// <summary>
        /// Obsahuje Type svých záznamů. Využívá se pro snadnější získání informace o typu při práci s prvky.
        /// </summary>
        public Type ItemType { get { return typeof(T); } }
        #endregion
        #region Příznaky a fyzické proměnné (_Data, _Deleted, _Flags)
        public bool HasNewItems { get { return this._FlagsGet(FlagBit.HasNewItems); } private set { this._FlagsSet(FlagBit.HasNewItems, value, true); } }
        public bool HasChangedItems { get { return this._FlagsGet(FlagBit.HasChangedItems); } private set { this._FlagsSet(FlagBit.HasChangedItems, value, true); } }
        public bool HasDeletedItems { get { return this._FlagsGet(FlagBit.HasDeletedItems); } private set { this._FlagsSet(FlagBit.HasDeletedItems, value, true); } }
        public bool HasSortedItems { get { return this._FlagsGet(FlagBit.HasSortedItems); } private set { this._FlagsSet(FlagBit.HasSortedItems, value, true); } }
        private bool _FlagsGet(FlagBit bit)
        {
            return ((this._Flags & bit) != 0);
        }
        private void _FlagsSet(FlagBit bit, bool value)
        {
            this._FlagsSet(bit, value, false);
        }
        private void _FlagsSet(FlagBit bit, bool value, bool onlySet)
        {
            bool status = _FlagsGet(bit);
            if (value && !status)
                this._Flags = this._Flags | bit;
            else if (!value && status && !onlySet)
                this._Flags = this._Flags & (bit ^ FlagBit.All);
        }
        [Flags]
        private enum FlagBit
        {
            None = 0,
            HasNewItems = 1,
            HasChangedItems = HasNewItems << 1,
            HasDeletedItems = HasChangedItems << 1,
            HasSortedItems = HasDeletedItems << 1,
            All = 0x7FFFFFFF
        }
        protected List<T> Items;
        protected List<T> Removed;
        private FlagBit _Flags;
        #endregion
        #region Proxy na List<T>, jejich součástí jsou konstruktory
        public DataList() { this.Items = new List<T>(); this.Removed = new List<T>(); }
        public DataList(IEnumerable<T> collection) { this.Items = new List<T>(collection); this.Removed = new List<T>(); }
        public DataList(int capacity) { this.Items = new List<T>(capacity); this.Removed = new List<T>(); }
        public int Capacity { get { return this.Items.Capacity; } set { this.Items.Capacity = value; } }
        public int Count { get { return this.Items.Count; } }
        public T this[int index] { get { return this.Items[index]; } set { this.Items[index] = value; this.HasChangedItems = true; } }
        public void Add(T item) { this.Items.Add(item); this.HasNewItems = true; }
        public void AddRange(IEnumerable<T> collection) { this.Items.AddRange(collection); this.HasNewItems = true; }
        public ReadOnlyCollection<T> AsReadOnly() { return this.Items.AsReadOnly(); }
        public int BinarySearch(T item) { return this.Items.BinarySearch(item); }
        public int BinarySearch(T item, IComparer<T> comparer) { return this.Items.BinarySearch(item, comparer); }
        public int BinarySearch(int index, int count, T item, IComparer<T> comparer) { return this.Items.BinarySearch(index, count, item, comparer); }
        public void Clear() { this.Items.Clear(); this.AcceptChanges(); }
        public bool Contains(T item) { return this.Items.Contains(item); }
        public List<TOutput> ConvertAll<TOutput>(Converter<T, TOutput> converter) { return this.Items.ConvertAll(converter); }
        public void CopyTo(T[] array) { this.Items.CopyTo(array); }
        public void CopyTo(T[] array, int arrayIndex) { this.Items.CopyTo(array, arrayIndex); }
        public void CopyTo(int index, T[] array, int arrayIndex, int count) { this.Items.CopyTo(index, array, arrayIndex, count); }
        public bool Exists(Predicate<T> match) { return this.Items.Exists(match); }
        public T Find(Predicate<T> match) { return this.Items.Find(match); }
        public List<T> FindAll(Predicate<T> match) { return this.Items.FindAll(match); }
        public int FindIndex(Predicate<T> match) { return this.Items.FindIndex(match); }
        public int FindIndex(int startIndex, Predicate<T> match) { return this.Items.FindIndex(startIndex, match); }
        public int FindIndex(int startIndex, int count, Predicate<T> match) { return this.Items.FindIndex(startIndex, count, match); }
        public T FindLast(Predicate<T> match) { return this.Items.FindLast(match); }
        public int FindLastIndex(Predicate<T> match) { return this.Items.FindLastIndex(match); }
        public int FindLastIndex(int startIndex, Predicate<T> match) { return this.Items.FindLastIndex(startIndex, match); }
        public int FindLastIndex(int startIndex, int count, Predicate<T> match) { return this.Items.FindLastIndex(startIndex, count, match); }
        public void ForEach(Action<T> action) { this.Items.ForEach(action); }
        public List<T>.Enumerator GetEnumerator() { return this.Items.GetEnumerator(); }
        public List<T> GetRange(int index, int count) { return this.Items.GetRange(index, count); }
        public int IndexOf(T item) { return this.Items.IndexOf(item); }
        public int IndexOf(T item, int index) { return this.Items.IndexOf(item, index); }
        public int IndexOf(T item, int index, int count) { return this.Items.IndexOf(item, index, count); }
        public void Insert(int index, T item) { this.Items.Insert(index, item); this.HasNewItems = true; }
        public void InsertRange(int index, IEnumerable<T> collection) { this.Items.InsertRange(index, collection); this.HasNewItems = true; }
        public int LastIndexOf(T item) { return this.Items.LastIndexOf(item); }
        public int LastIndexOf(T item, int index) { return this.Items.LastIndexOf(item, index); }
        public int LastIndexOf(T item, int index, int count) { return this.Items.LastIndexOf(item, index, count); }
        public bool Remove(T item) { bool result = this.Items.Remove(item); if (result) this.HasDeletedItems = true; return result; }
        public int RemoveAll(Predicate<T> match) { int result = this.Items.RemoveAll(match); if (result > 0) this.HasDeletedItems = true; return result; }
        public void RemoveAt(int index) { this.Items.RemoveAt(index); this.HasDeletedItems = true; }
        public void RemoveRange(int index, int count) { this.Items.RemoveRange(index, count); if (count > 0) this.HasDeletedItems = true; }
        public void Reverse() { this.Items.Reverse(); this.HasSortedItems = true; }
        public void Reverse(int index, int count) { this.Items.Reverse(index, count); this.HasSortedItems = true; }
        public void Sort() { this.Items.Sort(); this.HasSortedItems = true; }
        public void Sort(Comparison<T> comparison) { this.Items.Sort(comparison); this.HasSortedItems = true; }
        public void Sort(IComparer<T> comparer) { this.Items.Sort(comparer); this.HasSortedItems = true; }
        public void Sort(int index, int count, IComparer<T> comparer) { this.Items.Sort(index, count, comparer); this.HasSortedItems = true; }
        public T[] ToArray() { return this.Items.ToArray(); }
        public void TrimExcess() { this.Items.TrimExcess(); }
        public bool TrueForAll(Predicate<T> match) { return this.Items.TrueForAll(match); }
        #endregion
        #region Explicitní implementace všech interface
        int IList<T>.IndexOf(T item) { return this.Items.IndexOf(item); }
        void IList<T>.Insert(int index, T item) { this.Items.Insert(index, item); this.HasNewItems = true; }
        void IList<T>.RemoveAt(int index) { this.Items.RemoveAt(index); this.HasDeletedItems = true; }
        T IList<T>.this[int index] { get { return this.Items[index]; } set { this.Items[index] = value; } }
        void ICollection<T>.Add(T item) { this.Items.Add(item); this.HasNewItems = true; }
        void ICollection<T>.Clear() { this.Items.Clear(); this.AcceptChanges(); }
        bool ICollection<T>.Contains(T item){ return this.Items.Contains(item); }
        void ICollection<T>.CopyTo(T[] array, int arrayIndex) { this.Items.CopyTo(array, arrayIndex); }
        int ICollection<T>.Count { get { return this.Items.Count; } }
        bool ICollection<T>.IsReadOnly { get { return ((ICollection<T>)this.Items).IsReadOnly; } }
        bool ICollection<T>.Remove(T item) { return this.Items.Remove(item); this.HasDeletedItems = true; }
        IEnumerator<T> IEnumerable<T>.GetEnumerator() { return this.Items.GetEnumerator(); }
        IEnumerator IEnumerable.GetEnumerator() { return this.Items.GetEnumerator(); }
        void ICollection.CopyTo(Array array, int index) { ((ICollection)this.Items).CopyTo(array, index); }
        int ICollection.Count { get { return this.Items.Count; } }
        bool ICollection.IsSynchronized { get { return ((ICollection)this.Items).IsSynchronized; } }
        object ICollection.SyncRoot { get { return ((ICollection)this.Items).SyncRoot; } }
        int IList.Add(object value) { int result = ((IList)this.Items).Add(value); this.HasNewItems = true; return result; }
        void IList.Clear() { this.Items.Clear(); this.AcceptChanges(); }
        bool IList.Contains(object value) { return ((IList)this.Items).Contains(value); }
        int IList.IndexOf(object value) { return ((IList)this.Items).IndexOf(value); }
        void IList.Insert(int index, object value) { ((IList)this.Items).Insert(index, value); this.HasNewItems = true; }
        bool IList.IsFixedSize { get { return ((IList)this.Items).IsFixedSize; } }
        bool IList.IsReadOnly { get { return ((IList)this.Items).IsReadOnly; } }
        void IList.Remove(object value) { ((IList)this.Items).Remove(value); this.HasDeletedItems = true; }
        void IList.RemoveAt(int index) { ((IList)this.Items).RemoveAt(index); this.HasDeletedItems = true; }
        object IList.this[int index] { get { return ((IList)this.Items)[index]; } set { ((IList)this.Items)[index] = value; this.HasChangedItems = true; } }
        #endregion
    }
}
